using logging;

/*-----------------------------------------------------------------*\
	Public Class: You should use it anyway.
\*-----------------------------------------------------------------*/
// Here is the coffee.
abstract class CoffeeBase{
    protected string _name;
    public string name => _name;
    public int cost{get;set;}
}

class DarkRoast: CoffeeBase{
    public DarkRoast(){ _name = "DarkRoast"; cost = 5; }
}

class Espresso: CoffeeBase{
    public Espresso(){ _name = "Espresso"; cost = 6; }
}

class Decaf: CoffeeBase{
    public Decaf(){ _name = "Decaf"; cost = 7; }
}

// Here is the decorator.
abstract class Decorator{  // it's the same as CoffeeBase, but there is no relationship.
    protected string _name;
    public string name => _name;
    public int cost{get;set;}
}

class Sugar: Decorator{
    public Sugar(){ _name = "sugar"; cost = 1; }
}

class Milk: Decorator{
    public Milk(){ _name = "milk"; cost = 2; }
}

class Mocha: Decorator{
    public Mocha(){ _name = "mocha"; cost = 3; }
}

#if WAY_1
/*-----------------------------------------------------------------*\
    原始的装饰模式，需要重新定义Decorator类型。

    这个类很有趣，它叫做装饰类，但它的对象是 CoffeeBase 类型；
    实际上，Decorator2 更像是 CoffeeBase 的补丁。
    所以，对于新的Decorator，已然替换了概念——它不再是一个装饰对象，而是一个转换器。
    只不过，它将装饰与转换的功能耦合在了一起，有点似是而非。
\*-----------------------------------------------------------------*/
// if there is only one additional function for CoffeeBase, u don't need the abstract Decorator class.
abstract class CoffeeDecorator: CoffeeBase{  // Decorator class must inherit CoffeeBase.
    protected string name_decorator;
    protected int cost_decorator;
    public CoffeeDecorator(CoffeeBase semi_coffee){
        this.init_decorator();
        _name = semi_coffee.name + " with " + name_decorator;
        cost = semi_coffee.cost + cost_decorator;
    }

    protected abstract void init_decorator();
}

class SugarCoffee: CoffeeDecorator{  // u can inherit the CoffeeBase directly.
    public SugarCoffee(CoffeeBase semi_coffee): base(semi_coffee){ }
    protected override void init_decorator(){
        name_decorator = "sugar";
        cost_decorator = 1;
    }
}

class MilkCoffee: CoffeeDecorator{
    public MilkCoffee(CoffeeBase semi_coffee): base(semi_coffee){ }
    protected override void init_decorator(){
        name_decorator = "milk";
        cost_decorator = 2;
    }
}

class MochaCoffee: CoffeeDecorator{
    public MochaCoffee(CoffeeBase semi_coffee): base(semi_coffee){ }
    protected override void init_decorator(){
        name_decorator = "mocha";
        cost_decorator = 3;
    }
}

class Demo{
    public static void Main(){
        CoffeeBase coffee = new DarkRoast();
        coffee = new SugarCoffee(coffee);
        coffee = new MilkCoffee(coffee);
        coffee = new MochaCoffee(coffee);
        coffee = new SugarCoffee(coffee);
        Log.print("I got a cup of coffee: " + coffee.name);
        Log.print("Cost of the coffee: " + coffee.cost);  // should be 12
    }
}

/* 个人认为，尽管装饰器尝试给CoffeeBase的对象增加一个新的功能，
   原来的CoffeeBase对象还是更新成了诸如 SugarCoffee 等子类型。
   如果装饰的子类无限拓展，我们type(object)时所得到的类型将爆炸。
   更糟心的是，SugarCoffee、MilkCoffee的类型毫无意义，它仅仅告诉你，
   原来的CoffeeBase曾经最终被SugarCoffee装饰。
   相比而言，WAY_2 提供的方式，将Decorator解耦，而你得到的也仅仅是
   一个CoffeeWithDecorator对象，它依旧属于CoffeeBase类型。
   你还可以用ArrayList存储层发生在该对象上的装饰过程。
 */

#elif WAY_2
/*-----------------------------------------------------------------*\
    WAY_2: 这是解决该问题的一个最为合理的方式——创建一个新的子类，
    融合了CoffeeBase与CoffeeDecorator的对象，生成新的类型。
\*-----------------------------------------------------------------*/
class UdfCoffe: CoffeeBase{  // inherit or not, is import to think! it will be discussed later.
    /*  if you like, you could store the process.
        protected CoffeeBase _coffee;
        protected ArrayList al_decorator = new ArrayList(); */
    public UdfCoffe(CoffeeBase coffee){
        // _coffee = coffee;
        _name = coffee.name;
        cost = coffee.cost;
    }
    public void add_decorator(Decorator decorator){
        // al_decorator.Add(decorator);
        _name += " with " + decorator.name;
        cost += decorator.cost;
    }
}

class Demo{
    public static void Main(){
        var coffee = new UdfCoffe(new DarkRoast());
        coffee.add_decorator(new Sugar());
        coffee.add_decorator(new Milk());
        coffee.add_decorator(new Mocha());
        coffee.add_decorator(new Milk());
        Log.print("I got a cup of coffee: " + coffee.name);
        Log.print("Cost of the coffee: " + coffee.cost);  // should be 13
    }
}

/* 这里有一个重要的话题：UdfCoffee是否要继承CoffeeBase.

   从功能角度讲，你完全可以将 UdfCoffee 作为一个全新的类型使用，
   并定义其自身的接口，而不一定是 name or cost。

   但如果，你继承了CoffeeBase，这才能被称之为装饰模式！
   因为，如此而来，UdfCoffee 能够作为 CoffeeBase 对象使用。
   这正是装饰模式的设计意图：动态的为CoffeeBase增加功能/职责。
   Attach additional responsibilities to an object dynamically.
 */

#else
/*-----------------------------------------------------------------*\
    WAY_3: 如果你更喜欢装饰模式 object = new class(object) 的形式...
    该方式与 WAY_2 相同，只不过构造时完成CoffeeBase与Decorator的融合。
\*-----------------------------------------------------------------*/
class CoffeeWithDecorator: CoffeeBase{  // u don't need to inherit. read the discuss above.
    public CoffeeWithDecorator(CoffeeBase semi_coffee, Decorator decorator){
        _name = semi_coffee.name + " with " + decorator.name;
        cost = semi_coffee.cost + decorator.cost;
    }
}

class Demo{
    public static void Main(){
        CoffeeBase coffee = new DarkRoast();
        coffee = new CoffeeWithDecorator(coffee, new Sugar());
        coffee = new CoffeeWithDecorator(coffee, new Milk());
        coffee = new CoffeeWithDecorator(coffee, new Mocha());
        coffee = new CoffeeWithDecorator(coffee, new Mocha());
        Log.print("I got a cup of coffee: " + coffee.name);
        Log.print("Cost of the coffee: " + coffee.cost);  // should be 14
    }
}

#endif
